package zheezes.util;

import java.util.Comparator;

import zheezes.conv.ZHexNumToChs;
import zheezes.conv.ZIChsToNum;
import zheezes.conv.ZINumToChs;
import zheezes.visit.ZINumVisit;

/**
 * @author hcconquer@gmail.com
 */
public class ZArray {
	public static char[] toPrimitive(Character[] array) {
		if (array == null) {
			return null;
		}
		char[] parr = new char[array.length];
		for (int i = 0; i < array.length; i++) {
			parr[i] = array[i];
		}
		return parr;
	}
	
	public static Character[] toObject(char[] array) {
		if (array == null) {
			return null;
		}
		Character[] parr = new Character[array.length];
		for (int i = 0; i < array.length; i++) {
			parr[i] = array[i];
		}
		return parr;
	}
	
	public static int[] toPrimitive(Integer[] array) {
		if (array == null) {
			return null;
		}
		int[] parr = new int[array.length];
		for (int i = 0; i < array.length; i++) {
			parr[i] = array[i];
		}
		return parr;
	}
	
	public static Integer[] toObject(int[] array) {
		if (array == null) {
			return null;
		}
		Integer[] parr = new Integer[array.length];
		for (int i = 0; i < array.length; i++) {
			parr[i] = array[i];
		}
		return parr;
	}
	
	/**
	 * conv char array to byte array
	 * 
	 * @param dst
	 *         the byte array
	 * @param df
	 *         the dest offset
	 * @param src
	 *         the char array 
	 * @param sf
	 *         the src offset
	 * @param len
	 *         conv length
	 * @return conv num
	 */
	public static int charToByte(byte[] dst, int df, char[] src, int sf, int len) {
		if (dst == null || src == null || sf < 0 || len < 0) {
			return -1;
		}
		if (dst.length < len || src.length < sf + len) {
			return -1;
		}
		for (int i = 0; i < len; i++) {
			dst[i] = (byte) src[sf + i];
		}
		return len;
	}
	
	/**
	 * conv char array to byte array
	 * 
	 * @see ZArray#charToByte(byte[], int, char[], int, int)
	 * 
	 * @param src
	 *         the char array 
	 * @param len
	 *         conv length
	 * @return conv num
	 */
	public static byte[] charToByte(char[] src, int len) {
		if (src == null || len < 0 || src.length < len) {
			return null;
		}
		byte[] dst = new byte[len];
		charToByte(dst, 0, src, 0, len);
		return dst;
	}

	/**
	 * conv char array to byte array
	 * 
	 * @param dst
	 *         the char array
	 * @param df
	 *         the dest offset
	 * @param src
	 *         the byte array
	 * @param sf
	 *         the src offset
	 * @param len
	 *         conv length
	 * @return conv length
	 */
	public static int byteToChar(char[] dst, int df, byte[] src, int sf, int len) {
		if (dst == null || src == null || sf < 0 || len < 0) {
			return -1;
		}
		if (dst.length < len || src.length < sf + len) {
			return -1;
		}
		for (int i = 0; i < len; i++) {
			dst[i] = (char) src[sf + i];
		}
		return len;
	}
	
	/**
	 * conv char array to byte array
	 * 
	 * @see ZArray#byteToChar(char[], int, byte[], int, int)
	 * 
	 * @param src
	 *         the byte array
	 * @param len
	 *         conv length
	 * @return conv length
	 */
	public static char[] byteToChar(byte[] src, int len) {
		if (src == null || len < 0 || src.length < len) {
			return null;
		}
		char[] dst = new char[len];
		byteToChar(dst, 0, src, 0, len);
		return dst;
	}

	/**
	 * @return conv num
	 * 
	 *  char[]("1234") --> byte[](12, 34)        ZDecChsToInt   BCD
	 *  char[]("1234") --> byte[](0x12, 0x34)    ZHexChsToInt   ASCII
	 */
	public static int convChsToNum(byte[] dst, int df, char[] src, int sf, int len,
			ZIChsToNum conv) {
		if (dst == null || src == null || len % 2 != 0 || conv == null) {
			return -1;
		}
		if (dst.length < len / 2) {
			return -1;
		}
		char[] pair = new char[2];
		int n = 0;
		while (n < len / 2) {
			pair[0] = src[2 * (sf + n)];
			pair[1] = src[2 * (sf + n) + 1];
			Integer val = conv.conv(pair);
			if (val == null) {
				break;
			}
			dst[df + n] = val.byteValue();
			n++;
		}
		return n;
	}
	
	/**
	 * @return conv num
	 * 
	 *  byte[](12, 34)     --> char[]("1234")     ZDecIntToChs
	 *  byte[](0x12, 0x34) --> char[]("1234")     ZHexIntToChs
	 */
	public static int convNumToChs(char[] dst, int df, byte[] src, int sf, int len,
			ZINumToChs conv) {
		if (dst == null || src == null || conv == null) {
			return -1;
		}
		if (dst.length < df + len * 2 || src.length < sf + len) {
			return -1;
		}
		char[] pair = null;
		int n = 0;
		while (n < len) {
			pair = conv.conv(ZByte.positive(src[sf + n]));
			if (pair == null) {
				break;
			}
			dst[2 * (df + n)] = pair[0];
			dst[2 * (df + n) + 1] = pair[1];
			n++;
		}
		return n;
	}
	
	/**
	 *  char[]("1234") --> byte[](12, 34)        ZDecChsToInt   BCD
	 *  char[]("1234") --> byte[](0x12, 0x34)    ZHexChsToInt   ASCII
	 * 
	 * @param src
	 * @param len
	 * @param conv
	 * @return
	 */
	public static byte[] convChsToNum(char[] src, int len, ZIChsToNum conv) {
		if (src == null || len < 0 || conv == null || src.length < len) {
			return null;
		}
		byte[] dst = new byte[(len + 1) / 2];
		convChsToNum(dst, 0, src, 0, len, conv);
		return dst;
	}
	
	/**
	 * 	byte[](12, 34)     --> char[]("1234")     ZDecIntToChs
	 *  byte[](0x12, 0x34) --> char[]("1234")     ZHexIntToChs
	 * 
	 * @param src
	 * @param len
	 * @param conv
	 * @return
	 */
	public static char[] convNumToChs(byte[] src, int len, ZINumToChs conv) {
		if (src == null || len < 0 || conv == null || src.length < len) {
			return null;
		}
		char[] dst = new char[len * 2];
		convNumToChs(dst, 0, src, 0, len, conv);
		return dst;
	}
	
	public static int convChsToNum(byte[] dst, char[] src, int len,
			ZIChsToNum conv) {
		return convChsToNum(dst, 0, src, 0, len, conv);
	}
	
	public static int convNumToChs(char[] dst, byte[] src, int len,
			ZINumToChs conv) {
		return convNumToChs(dst, 0, src, 0, len, conv);
	}
	
	/**
	 * traverse a byte array
	 * 
	 * @param arr
	 *         byte array
	 * @param visit
	 *         visit function, return return < 0 on error
	 * @param bOnErr
	 *         continue on error by visit return < 0
	 * @return succ 0, other -1
	 */
	public static int traverse(byte[] arr, ZINumVisit visit, boolean bOnErr) {
		if (arr == null || visit == null) {
			return -1;
		}
		for (int i = 0; i < arr.length; i++) {
			int ret = visit.visit(arr[i]);
			if (ret < 0) {
				if (!bOnErr) {
					return ret;
				}
			}
		}
		return 0;
	}

	/**
	 * traverse a char array
	 * 
	 * @param arr
	 *         byte array
	 * @param visit
	 *         visit function, return return < 0 on error
	 * @param bOnErr
	 *         continue on error by visit return < 0
	 * @return succ 0, other -1
	 */
	public static int traverse(char[] arr, ZINumVisit visit, boolean bOnErr) {
		if (arr == null || visit == null) {
			return -1;
		}
		for (int i = 0; i < arr.length; i++) {
			int ret = visit.visit(arr[i]);
			if (ret < 0) {
				if (!bOnErr) {
					return ret;
				}
			}
		}
		return 0;
	}
	
	/**
	 * traverse a int array
	 * 
	 * @param arr
	 *         byte array
	 * @param visit
	 *         visit function, return return < 0 on error
	 * @param bOnErr
	 *         continue on error by visit return < 0
	 * @return succ 0, other -1
	 */
	public static int traverse(int[] arr, ZINumVisit visit, boolean bOnErr) {
		if (arr == null || visit == null) {
			return -1;
		}
		for (int i = 0; i < arr.length; i++) {
			int ret = visit.visit(arr[i]);
			if (ret < 0) {
				if (!bOnErr) {
					return ret;
				}
			}
		}
		return 0;
	}

	/**
	 * find the key in array
	 * 
	 * @param array
	 *         Object array
	 * @param key
	 *         Object key
	 * @return the index in array or -1
	 */
	public static int find(char[] array, char key) {
		return find(toObject(array), key, new Comparator<Character>() {
			@Override
			public int compare(Character p, Character q) {
				return p - q;
			}
		});
	}
	
	/**
	 * find the key in array, match by equals
	 * 
	 * @param array
	 *         Object array
	 * @param key
	 *         Object key
	 * @return the index in array or -1
	 */
	public static int find(Object[] array, Object key) {
		return find(array, key, new Comparator<Object>() {
			@Override
			public int compare(Object p, Object q) {
				return p.equals(q) ? 0 : -1;
			}
		});
	}
	
	public static <T> int find(T[] array, T key, Comparator<T> cmp) {
		if (array == null || key == null) {
			return -1;
		}
		int num = array.length;
		for (int idx = 0; idx < num; idx++) {
			if (cmp.compare(key, array[idx]) == 0) {
				return idx;
			}
		}
		return -1;
	}
	
	public static <T> int find(T[][] array, int col, T key, Comparator<T> cmp) {
		if (array == null || key == null) {
			return -1;
		}
		int num = array.length;
		for (int idx = 0; idx < num; idx++) {
			if (cmp.compare(key, array[idx][col]) == 0) {
				return idx;
			}
		}
		return -1;
	}
	
	public static void main(String[] args) {
		ZLog4j.configConsole();
		/*
		char[] cs = "10ff".toCharArray();
		byte[] bs = { 1, 2, 5, 7 };
		traverse(bs, new ZNumPrintHexAscWBVisit());
		System.out.println();
		int num = convChsToNum(bs, 0, cs, 0, cs.length / 2, new ZHexChsToNum());
		System.out.printf("conv: %d\n", num);
		traverse(bs, new ZNumPrintHexAscWBVisit());
		System.out.println();
		*/
		byte[] bs = { 00, 00, (byte) 0x81 };
		char[] cs = ZArray.convNumToChs(bs, bs.length, new ZHexNumToChs());
		String value = new String(cs);
		System.out.println(value);
	}
}
